Final EDA

Introduction

Credit Card Defaults can occur due to a number of reasons. According to the use case defintion

A default can occur when a borrower is unable to make timely payments, misses payments, or avoids/stops making payments

The question that we are concerend with is

Which priority clients have the highest risk of credit card default?

Libraries

Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.2.1     ✓ purrr   0.3.3
✓ tibble  2.1.3     ✓ dplyr   0.8.4
✓ tidyr   1.0.0     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.4.0
── Conflicts ──────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths

Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Loading Data

Structure of the dataframe

train_df <- train_df %>% mutate_at(vars(contains("PAY_")),factor)
print(str(train_df))
'data.frame':   24000 obs. of  25 variables:
 $ Client_ID         : Factor w/ 24000 levels "A100","A1000",..: 8869 17782 18686 19584 20487 22231 23127 865 1758 2633 ...
 $ Balance_Limit_V1  : Factor w/ 8 levels " 500K","1.5M",..: 4 4 3 6 4 8 3 3 1 4 ...
 $ Gender            : Factor w/ 2 levels "F","M": 2 1 1 1 1 1 2 1 2 2 ...
 $ EDUCATION_STATUS  : Factor w/ 3 levels "Graduate","High School",..: 1 2 2 1 1 1 3 2 3 3 ...
 $ MARITAL_STATUS    : Factor w/ 2 levels "Other","Single": 1 1 2 2 1 2 2 1 1 2 ...
 $ AGE               : Factor w/ 4 levels "31-45","46-65",..: 1 3 1 1 1 1 3 3 1 2 ...
 $ PAY_JULY          : Factor w/ 11 levels "-2","-1","0",..: 2 3 7 5 5 3 4 5 3 3 ...
 $ PAY_AUG           : Factor w/ 11 levels "-2","-1","0",..: 2 2 6 3 5 3 5 5 3 3 ...
 $ PAY_SEP           : Factor w/ 11 levels "-2","-1","0",..: 2 2 5 3 3 3 5 5 3 5 ...
 $ PAY_OCT           : Factor w/ 11 levels "-2","-1","0",..: 2 2 5 3 3 3 5 3 5 3 ...
 $ PAY_NOV           : Factor w/ 10 levels "-2","-1","0",..: 2 2 1 3 3 3 4 3 3 3 ...
 $ PAY_DEC           : Factor w/ 10 levels "-2","-1","0",..: 2 3 1 3 3 3 4 4 3 3 ...
 $ DUE_AMT_JULY      : int  3248 353351 16681 90457 429556 361284 8991 51836 198579 268551 ...
 $ DUE_AMT_AUG       : int  3389 151818 16082 92848 419466 364802 8515 55828 204634 282726 ...
 $ DUE_AMT_SEP       : int  6004 26948 15477 95193 429785 366703 11698 54241 218092 274123 ...
 $ DUE_AMT_OCT       : int  39418 43530 0 97309 435354 353910 11173 55325 212970 221148 ...
 $ DUE_AMT_NOV       : int  162772 80811 0 100353 445271 356117 12030 59272 213654 222936 ...
 $ DUE_AMT_DEC       : int  -13982 124590 0 102740 453899 358845 12647 57976 217992 224276 ...
 $ PAID_AMT_JULY     : int  3437 151818 0 3855 0 16632 0 5521 9240 26565 ...
 $ PAID_AMT_AUG      : int  6004 46200 0 3890 20790 18480 3696 0 17325 0 ...
 $ PAID_AMT_SEP      : int  39418 43530 0 3696 16170 12728 0 1984 0 8184 ...
 $ PAID_AMT_OCT      : int  162772 80811 0 4620 17325 13398 1386 4844 6930 8547 ...
 $ PAID_AMT_NOV      : int  0 942 0 4049 16401 13860 1155 0 11550 8194 ...
 $ PAID_AMT_DEC      : int  538165 33666 0 3918 17325 12705 0 2523 11550 7311 ...
 $ NEXT_MONTH_DEFAULT: Factor w/ 2 levels "0","1": 1 1 2 2 1 1 1 2 1 1 ...
NULL

Summary of the dataframe

print(summary(train_df %>% subset(select=-c(Client_ID))))
 Balance_Limit_V1 Gender       EDUCATION_STATUS MARITAL_STATUS           AGE           PAY_JULY    
 1M     :5951     F: 9540   Graduate   : 8478   Other :13070   31-45       :12124   0      :11788  
 200K   :5159     M:14460   High School: 3925   Single:10930   46-65       : 4150   -1     : 4534  
 100K   :3449               Other      :11597                  Less than 30: 7638   1      : 2956  
 400K   :3065                                                  More than 65:   88   -2     : 2205  
  500K  :2790                                                                       2      : 2140  
 300K   :2411                                                                       3      :  258  
 (Other):1175                                                                       (Other):  119  
    PAY_AUG         PAY_SEP         PAY_OCT         PAY_NOV         PAY_DEC       DUE_AMT_JULY    
 0      :12562   0      :12609   0      :13199   0      :13581   0      :13045   Min.   :-382490  
 -1     : 4847   -1     : 4730   -1     : 4514   -1     : 4415   -1     : 4591   1st Qu.:   8246  
 2      : 3150   -2     : 3240   -2     : 3453   -2     : 3605   -2     : 3885   Median :  51568  
 -2     : 3014   2      : 3097   2      : 2548   2      : 2124   2      : 2231   Mean   : 118870  
 3      :  271   3      :  198   3      :  156   3      :  145   3      :  148   3rd Qu.: 156274  
 4      :   84   4      :   65   4      :   53   4      :   69   4      :   39   Max.   :2228020  
 (Other):   72   (Other):   61   (Other):   77   (Other):   61   (Other):   61                    
  DUE_AMT_AUG       DUE_AMT_SEP       DUE_AMT_OCT       DUE_AMT_NOV       DUE_AMT_DEC     
 Min.   :-161185   Min.   :-142079   Min.   :-392700   Min.   :-187882   Min.   :-784483  
 1st Qu.:   6969   1st Qu.:   6238   1st Qu.:   5429   1st Qu.:   4180   1st Qu.:   2913  
 Median :  48717   Median :  46412   Median :  44105   Median :  41863   Median :  39409  
 Mean   : 114073   Mean   : 109244   Mean   : 100357   Mean   :  93777   Mean   :  90341  
 3rd Qu.: 148905   3rd Qu.: 140162   3rd Qu.: 126975   3rd Qu.: 116926   3rd Qu.: 114435  
 Max.   :2272881   Max.   :3844046   Max.   :2059564   Max.   :2141765   Max.   :2221444  
                                                                                          
 PAID_AMT_JULY      PAID_AMT_AUG      PAID_AMT_SEP      PAID_AMT_OCT      PAID_AMT_NOV   
 Min.   :      0   Min.   :      0   Min.   :      0   Min.   :      0   Min.   :     0  
 1st Qu.:   2310   1st Qu.:   1956   1st Qu.:    901   1st Qu.:    693   1st Qu.:   610  
 Median :   4920   Median :   4646   Median :   4197   Median :   3465   Median :  3465  
 Mean   :  13306   Mean   :  13867   Mean   :  12093   Mean   :  11225   Mean   : 11175  
 3rd Qu.:  11605   3rd Qu.:  11550   3rd Qu.:  10626   3rd Qu.:   9360   3rd Qu.:  9412  
 Max.   :2017905   Max.   :3890638   Max.   :2069852   Max.   :1434510   Max.   :965557  
                                                                                         
  PAID_AMT_DEC     NEXT_MONTH_DEFAULT
 Min.   :      0   0:18670           
 1st Qu.:    307   1: 5330           
 Median :   3465                     
 Mean   :  12301                     
 3rd Qu.:   9252                     
 Max.   :1221218                     
                                     

Categorical Variables

First let’s explore the Categorical variables. Since we are interested in what are the factors that affect the risk of credit card default, we shall be exploring considering the two levels of the response variable

Gender

train_df$Gender <- factor(train_df$Gender) # converts to a categorical variable
train_df$NEXT_MONTH_DEFAULT <- factor(train_df$NEXT_MONTH_DEFAULT) # converts to a categorical variable
p1 <- ggplot(data=train_df %>% 
               mutate(Gender = case_when(Gender == "M" ~ "Male",
                                         Gender == "F" ~ "Female")), 
             aes(x=factor(1), stat="bin", fill=Gender)) + 
  geom_bar(position="fill") # Stacked bar chart
p1 <- p1 + ggtitle("Gender by Next Month Default") + xlab("") + ylab("NEXT_MONTH_DEFAULT") # Adds titles
p1 <- p1 + facet_grid(facets=. ~ NEXT_MONTH_DEFAULT) # Side by side bar chart
p1 <- p1 + coord_polar(theta="y") # side by side pie chart
p1

As it can be clearly seen here, gender does not have a significant difference when compared with defaulting credit cards and not defaulting.

Marital status

train_df$Gender <- factor(train_df$MARITAL_STATUS) # converts to a categorical variable
train_df$NEXT_MONTH_DEFAULT <- factor(train_df$NEXT_MONTH_DEFAULT) # converts to a categorical variable
p2 <- ggplot(data=train_df, aes(x=factor(1), stat="bin", fill=MARITAL_STATUS)) + 
  geom_bar(position="fill") # Stacked bar chart
p2 <- p2 + ggtitle("Marital Status by Next Month Default") + xlab("") + ylab("NEXT_MONTH_DEFAULT") # Adds titles
p2 <- p2 + facet_grid(facets=. ~ NEXT_MONTH_DEFAULT) # Side by side bar chart
p2 <- p2 + coord_polar(theta="y") # side by side pie chart
p2

As it can be clearly seen here, marital status does not have a significant difference when compared with defaulting credit cards and not defaulting. -Hence we have clear reasons for removing the variables gender and marital status_

Education Status

education_response_ct <- CrossTable(train_df$EDUCATION_STATUS,
                               train_df$NEXT_MONTH_DEFAULT ,
                               prop.r= FALSE,
                               prop.c=TRUE,
                               prop.chisq=FALSE, 
                               chisq = TRUE,
                               prop.t=TRUE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Col Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  24000 

 
                          | train_df$NEXT_MONTH_DEFAULT 
train_df$EDUCATION_STATUS |         0 |         1 | Row Total | 
--------------------------|-----------|-----------|-----------|
                 Graduate |      6828 |      1650 |      8478 | 
                          |     0.366 |     0.310 |           | 
                          |     0.284 |     0.069 |           | 
--------------------------|-----------|-----------|-----------|
              High School |      2939 |       986 |      3925 | 
                          |     0.157 |     0.185 |           | 
                          |     0.122 |     0.041 |           | 
--------------------------|-----------|-----------|-----------|
                    Other |      8903 |      2694 |     11597 | 
                          |     0.477 |     0.505 |           | 
                          |     0.371 |     0.112 |           | 
--------------------------|-----------|-----------|-----------|
             Column Total |     18670 |      5330 |     24000 | 
                          |     0.778 |     0.222 |           | 
--------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  63.292     d.f. =  2     p =  1.804334e-14 


 

Plotting

ggplot(data=as.data.frame(education_response_ct$prop.tbl),
       aes(x=x,y=Freq,fill=y))+
  geom_bar(stat = "identity",position = "fill")+
  scale_y_continuous(labels = scales::percent_format())+
  coord_flip()+theme_minimal()+
  xlab("Education Status")+ylab("Percentages")

Even though it is not evident that there is a relationship between education status and the response variable, the chi square test confirms that there is an association between the two variables ### Balance Limit

blv <- train_df%>% 
  mutate(Balance_Limit_V1=trimws(Balance_Limit_V1)) %>% 
  subset(select=Balance_Limit_V1)
train_df$Balance_Limit_V1 <- factor(as.factor(blv$Balance_Limit_V1),
                          levels=c("100K","200K","300K","400K","500K","1M","1.5M","2.5M"))
balance_response_ct <- CrossTable(train_df$Balance_Limit_V1 ,
                               train_df$NEXT_MONTH_DEFAULT ,
                               prop.r= FALSE,
                               prop.c=TRUE,
                               prop.chisq=FALSE, 
                               chisq = TRUE,
                               prop.t=TRUE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Col Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  24000 

 
                          | train_df$NEXT_MONTH_DEFAULT 
train_df$Balance_Limit_V1 |         0 |         1 | Row Total | 
--------------------------|-----------|-----------|-----------|
                     100K |      2207 |      1242 |      3449 | 
                          |     0.118 |     0.233 |           | 
                          |     0.092 |     0.052 |           | 
--------------------------|-----------|-----------|-----------|
                     200K |      3805 |      1354 |      5159 | 
                          |     0.204 |     0.254 |           | 
                          |     0.159 |     0.056 |           | 
--------------------------|-----------|-----------|-----------|
                     300K |      1805 |       606 |      2411 | 
                          |     0.097 |     0.114 |           | 
                          |     0.075 |     0.025 |           | 
--------------------------|-----------|-----------|-----------|
                     400K |      2469 |       596 |      3065 | 
                          |     0.132 |     0.112 |           | 
                          |     0.103 |     0.025 |           | 
--------------------------|-----------|-----------|-----------|
                     500K |      2289 |       501 |      2790 | 
                          |     0.123 |     0.094 |           | 
                          |     0.095 |     0.021 |           | 
--------------------------|-----------|-----------|-----------|
                       1M |      5061 |       890 |      5951 | 
                          |     0.271 |     0.167 |           | 
                          |     0.211 |     0.037 |           | 
--------------------------|-----------|-----------|-----------|
                     1.5M |      1000 |       138 |      1138 | 
                          |     0.054 |     0.026 |           | 
                          |     0.042 |     0.006 |           | 
--------------------------|-----------|-----------|-----------|
                     2.5M |        34 |         3 |        37 | 
                          |     0.002 |     0.001 |           | 
                          |     0.001 |     0.000 |           | 
--------------------------|-----------|-----------|-----------|
             Column Total |     18670 |      5330 |     24000 | 
                          |     0.778 |     0.222 |           | 
--------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  736.0712     d.f. =  7     p =  1.148874e-154 


 

Plotting

ggplot(data=as.data.frame(balance_response_ct$prop.tbl),
       aes(fill=y,y=Freq,x=x))+
  geom_bar(stat = "identity",position = "fill")+
  scale_y_continuous(labels = scales::percent_format())+
  coord_flip()+theme_minimal()+
  xlab("Balance Limit")+ylab("Percentages")

Here it is clear that a client with a lower balance limit has a higher chance of getting a credit card default. Also from the chi square test it is clear that with a p-value less than alpha=0.05 we can reject H0 and come to the conclusion that there is balance limit and the response variable are not independent

Payment due variable

ggplot(train_df %>%
  gather(PAY_JULY , PAY_AUG , PAY_SEP , PAY_OCT , PAY_NOV , PAY_DEC , key = "PAY_MONTH",value = "NO_MONTHS_PAY"), aes(x = NO_MONTHS_PAY, y = PAY_MONTH, fill = NEXT_MONTH_DEFAULT)) +
  geom_density_ridges(scale=0.9,alpha=0.7) +
  theme_ridges()+
  labs(fill='Credit Default') 

Quantitative Variables

Due amounts

ggplot(data=gather(train_df,key,value,c("DUE_AMT_JULY","DUE_AMT_AUG",
                                        "DUE_AMT_SEP","DUE_AMT_OCT",
                                        "DUE_AMT_NOV","DUE_AMT_DEC")) %>% 
         mutate(key = factor(key,levels=c("DUE_AMT_JULY","DUE_AMT_AUG",
                                        "DUE_AMT_SEP","DUE_AMT_OCT",
                                        "DUE_AMT_NOV","DUE_AMT_DEC"))),
       aes(x = key,y = value,fill=NEXT_MONTH_DEFAULT))+
  geom_boxplot()+
  scale_x_discrete(labels=c("JULY","AUG","SEP","OCT","NOV","DEC"))

Further visualizations

Due amount as a ratio of credit limit

train_df <- train_df %>% mutate(
  Balance_Credit_Limit_Numeric = case_when(
    Balance_Limit_V1 == "100K" ~ 100000,
    Balance_Limit_V1 == "200K" ~ 200000,
    Balance_Limit_V1 == "300K" ~ 300000,
    Balance_Limit_V1 == "400K" ~ 400000,
    Balance_Limit_V1 == "500K" ~ 500000,
    Balance_Limit_V1 == "1M" ~ 1000000,
    Balance_Limit_V1 == "1.5M" ~ 1500000,
    Balance_Limit_V1 == "2M" ~ 2000000,
    Balance_Limit_V1 == "2.5M" ~ 2500000
  )
) %>%
  mutate(
    Due_Credit_Lim_JULY=(DUE_AMT_JULY/Balance_Credit_Limit_Numeric) * 100,
    Due_Credit_Lim_AUG=(DUE_AMT_AUG/Balance_Credit_Limit_Numeric) * 100,
    Due_Credit_Lim_SEP=(DUE_AMT_SEP/Balance_Credit_Limit_Numeric) * 100,
    Due_Credit_Lim_OCT=(DUE_AMT_OCT/Balance_Credit_Limit_Numeric) * 100,
    Due_Credit_Lim_NOV=(DUE_AMT_NOV/Balance_Credit_Limit_Numeric) * 100,
    Due_Credit_Lim_DEC=(DUE_AMT_DEC/Balance_Credit_Limit_Numeric) * 100
  ) %>%
  na.omit()
ggplot(data=gather(train_df,key,value,c("Due_Credit_Lim_JULY","Due_Credit_Lim_AUG",
                                        "Due_Credit_Lim_SEP","Due_Credit_Lim_OCT",
                                        "Due_Credit_Lim_NOV","Due_Credit_Lim_DEC")) %>%
         mutate(key = factor(key,levels=c("Due_Credit_Lim_JULY","Due_Credit_Lim_AUG",
                                        "Due_Credit_Lim_SEP","Due_Credit_Lim_OCT",
                                        "Due_Credit_Lim_NOV","Due_Credit_Lim_DEC"))),
       aes(x=key,y=value))+
  geom_boxplot(aes(fill=NEXT_MONTH_DEFAULT))+
  coord_cartesian(ylim=c(0,200))+
  scale_x_discrete(labels=c("JULY","AUG","SEP","OCT","NOV","DEC"))

Balance feature

Here we consider the balance as the Due amount - Paid amount

train_df$balance_july <- train_df$DUE_AMT_JULY - train_df$PAID_AMT_JULY
train_df$balance_aug <- train_df$DUE_AMT_AUG - train_df$PAID_AMT_AUG
train_df$balance_sep <- train_df$DUE_AMT_SEP - train_df$PAID_AMT_SEP
train_df$balance_oct <- train_df$DUE_AMT_OCT - train_df$PAID_AMT_OCT
train_df$balance_nov <- train_df$DUE_AMT_NOV - train_df$PAID_AMT_NOV
train_df$balance_dec <- train_df$DUE_AMT_DEC - train_df$PAID_AMT_DEC

ggplot(data=gather(train_df,key,value,c("balance_july","balance_aug",
                                        "balance_sep","balance_oct",
                                        "balance_nov","balance_dec")) %>%
         mutate(key = factor(key,levels =c("balance_july","balance_aug",
                                        "balance_sep","balance_oct",
                                        "balance_nov","balance_dec"))),
       aes(x = key,y = value))+
  geom_boxplot(aes(fill=NEXT_MONTH_DEFAULT))+
  theme_minimal()+
  scale_x_discrete(labels=c("JULY","AUG","SEP","OCT","NOV","DEC"))

Balance as a ratio of of credit limit

train_df <- train_df %>% mutate(
    balance_Credit_Lim_JULY=(balance_july/Balance_Credit_Limit_Numeric) * 100,
    balance_Credit_Lim_AUG=(balance_aug/Balance_Credit_Limit_Numeric) * 100,
    balance_Credit_Lim_SEP=(balance_sep/Balance_Credit_Limit_Numeric) * 100,
    balance_Credit_Lim_OCT=(balance_oct/Balance_Credit_Limit_Numeric) * 100,
    balance_Credit_Lim_NOV=(balance_nov/Balance_Credit_Limit_Numeric) * 100,
    balance_Credit_Lim_DEC=(balance_dec/Balance_Credit_Limit_Numeric) * 100
  ) %>%
  na.omit()
ggplot(data=gather(train_df,key,value,c("balance_Credit_Lim_JULY","balance_Credit_Lim_AUG",
                                        "balance_Credit_Lim_SEP","balance_Credit_Lim_OCT",
                                        "balance_Credit_Lim_NOV","balance_Credit_Lim_DEC")) %>%
         mutate(key = factor(key,levels=c("balance_Credit_Lim_JULY","balance_Credit_Lim_AUG",
                                        "balance_Credit_Lim_SEP","balance_Credit_Lim_OCT",
                                        "balance_Credit_Lim_NOV","balance_Credit_Lim_DEC"))),
       aes(x=key,y=value))+
  geom_boxplot(aes(fill=NEXT_MONTH_DEFAULT))+
  coord_cartesian(ylim=c(0,200))+
  scale_x_discrete(labels=c("JULY","AUG","SEP","OCT","NOV","DEC"))

Pay delay with the Paid value

ggplot(data=train_df%>%
         gather(pay_key,pay_value,c("PAY_JULY","PAY_AUG",
                                        "PAY_SEP","PAY_OCT",
                                        "PAY_NOV","PAY_DEC")) %>%
         gather(paid_key,paid_value,c("PAID_AMT_JULY","PAID_AMT_AUG",
                                      "PAID_AMT_SEP","PAID_AMT_OCT",
                                      "PAID_AMT_NOV","PAID_AMT_DEC")) %>%
         mutate(pay_key = factor(pay_key,levels=c("PAY_JULY","PAY_AUG",
                                        "PAY_SEP","PAY_OCT",
                                        "PAY_NOV","PAY_DEC")),
                pay_value=factor(pay_value,levels=seq(-2,9))),
       aes(y=paid_value,x=pay_value,fill=NEXT_MONTH_DEFAULT))+
  # geom_line(aes(color=NEXT_MONTH_DEFAULT))+
  geom_bar(position="dodge", stat="identity")+
  facet_grid(pay_key ~ .) + theme_minimal()

Correlation matrix plot

Disregard this as it is too cluttered

train_df_to_cor <- train_df %>% mutate_if(is.factor,as.numeric)
corrmat <- cor(train_df_to_cor,method="spearman")
melted <- melt(corrmat) %>%
  mutate(text = paste0("x: ", Var1, "\n", "y: ", Var2, "\n", "Value: ",round(value,2), "\n"))
p <- ggplot(melted, aes(Var1, Var2, fill= value, text=text)) + 
  geom_tile()
p

ggplotly(p, tooltip="text")
LS0tCnRpdGxlOiAiRGF0YSBTdG9ybSAxLjAiCnN1YnRpdGxlOiAiZHMxMDQ4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgojIEZpbmFsIEVEQQoKIyMgSW50cm9kdWN0aW9uCgpDcmVkaXQgQ2FyZCBEZWZhdWx0cyBjYW4gb2NjdXIgZHVlIHRvIGEgbnVtYmVyIG9mIHJlYXNvbnMuIEFjY29yZGluZyB0byB0aGUgdXNlIGNhc2UgZGVmaW50aW9uCgo+IEEgZGVmYXVsdCBjYW4gb2NjdXIgd2hlbiBhIGJvcnJvd2VyIGlzIHVuYWJsZSB0byBtYWtlIHRpbWVseSBwYXltZW50cywgbWlzc2VzIHBheW1lbnRzLCBvciBhdm9pZHMvc3RvcHMgbWFraW5nIHBheW1lbnRzCgpUaGUgcXVlc3Rpb24gdGhhdCB3ZSBhcmUgY29uY2VyZW5kIHdpdGggaXMgCgo+IFdoaWNoIHByaW9yaXR5IGNsaWVudHMgaGF2ZSB0aGUgaGlnaGVzdCByaXNrIG9mIGNyZWRpdCBjYXJkIGRlZmF1bHQ/CgoqTGlicmFyaWVzKgpgYGB7cixlY2hvPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnbW9kZWxzKQpsaWJyYXJ5KGdncmlkZ2VzKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHBsb3RseSkKYGBgCiMjIExvYWRpbmcgRGF0YQoKYGBge3IsZWNobz1GfQp0cmFpbl9kZiA8LSByZWFkLmNzdigiLi4vY3JlZGl0X2NhcmRfZGVmYXVsdF90cmFpbi5jc3YiKQp0cmFpbl9kZiRORVhUX01PTlRIX0RFRkFVTFQgPC0gYXMuZmFjdG9yKHRyYWluX2RmJE5FWFRfTU9OVEhfREVGQVVMVCkKdGVzdF9kZiA8LSByZWFkLmNzdigiLi4vY3JlZGl0X2NhcmRfZGVmYXVsdF90ZXN0LmNzdiIpCmBgYAoKU3RydWN0dXJlIG9mIHRoZSBkYXRhZnJhbWUKYGBge3J9CnRyYWluX2RmIDwtIHRyYWluX2RmICU+JSBtdXRhdGVfYXQodmFycyhjb250YWlucygiUEFZXyIpKSxmYWN0b3IpCnByaW50KHN0cih0cmFpbl9kZikpCmBgYAoKU3VtbWFyeSBvZiB0aGUgZGF0YWZyYW1lCmBgYHtyfQpwcmludChzdW1tYXJ5KHRyYWluX2RmICU+JSBzdWJzZXQoc2VsZWN0PS1jKENsaWVudF9JRCkpKSkKYGBgCiMjIENhdGVnb3JpY2FsIFZhcmlhYmxlcwoKRmlyc3QgbGV0J3MgZXhwbG9yZSB0aGUgQ2F0ZWdvcmljYWwgdmFyaWFibGVzLiBTaW5jZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiB3aGF0IGFyZSB0aGUgZmFjdG9ycyB0aGF0IGFmZmVjdCB0aGUgcmlzayBvZiBjcmVkaXQgY2FyZCBkZWZhdWx0LCB3ZSBzaGFsbCBiZSBleHBsb3JpbmcgY29uc2lkZXJpbmcgdGhlIHR3byBsZXZlbHMgb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlCgojIyMgR2VuZGVyCmBgYHtyfQp0cmFpbl9kZiRHZW5kZXIgPC0gZmFjdG9yKHRyYWluX2RmJEdlbmRlcikgIyBjb252ZXJ0cyB0byBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlCnRyYWluX2RmJE5FWFRfTU9OVEhfREVGQVVMVCA8LSBmYWN0b3IodHJhaW5fZGYkTkVYVF9NT05USF9ERUZBVUxUKSAjIGNvbnZlcnRzIHRvIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUKcDEgPC0gZ2dwbG90KGRhdGE9dHJhaW5fZGYgJT4lIAogICAgICAgICAgICAgICBtdXRhdGUoR2VuZGVyID0gY2FzZV93aGVuKEdlbmRlciA9PSAiTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZGVyID09ICJGIiB+ICJGZW1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5FWFRfTU9OVEhfREVGQVVMVCA9PSAwIH4gIk5vdCBEZWZhdWx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSwgCiAgICAgICAgICAgICBhZXMoeD1mYWN0b3IoMSksIHN0YXQ9ImJpbiIsIGZpbGw9R2VuZGVyKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIpICMgU3RhY2tlZCBiYXIgY2hhcnQKcDEgPC0gcDEgKyBnZ3RpdGxlKCJHZW5kZXIgYnkgTmV4dCBNb250aCBEZWZhdWx0IikgKyB4bGFiKCIiKSArIHlsYWIoIk5FWFRfTU9OVEhfREVGQVVMVCIpICMgQWRkcyB0aXRsZXMKcDEgPC0gcDEgKyBmYWNldF9ncmlkKGZhY2V0cz0uIH4gTkVYVF9NT05USF9ERUZBVUxUKSAjIFNpZGUgYnkgc2lkZSBiYXIgY2hhcnQKcDEgPC0gcDEgKyBjb29yZF9wb2xhcih0aGV0YT0ieSIpICMgc2lkZSBieSBzaWRlIHBpZSBjaGFydApwMQpgYGAKXypBcyBpdCBjYW4gYmUgY2xlYXJseSBzZWVuIGhlcmUsIGdlbmRlciBkb2VzIG5vdCBoYXZlIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSB3aGVuIGNvbXBhcmVkIHdpdGggZGVmYXVsdGluZyBjcmVkaXQgY2FyZHMgYW5kIG5vdCBkZWZhdWx0aW5nLiAqXwoKIyMjIE1hcml0YWwgc3RhdHVzCmBgYHtyfQp0cmFpbl9kZiRHZW5kZXIgPC0gZmFjdG9yKHRyYWluX2RmJE1BUklUQUxfU1RBVFVTKSAjIGNvbnZlcnRzIHRvIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUKdHJhaW5fZGYkTkVYVF9NT05USF9ERUZBVUxUIDwtIGZhY3Rvcih0cmFpbl9kZiRORVhUX01PTlRIX0RFRkFVTFQpICMgY29udmVydHMgdG8gYSBjYXRlZ29yaWNhbCB2YXJpYWJsZQpwMiA8LSBnZ3Bsb3QoZGF0YT10cmFpbl9kZiwgYWVzKHg9ZmFjdG9yKDEpLCBzdGF0PSJiaW4iLCBmaWxsPU1BUklUQUxfU1RBVFVTKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIpICMgU3RhY2tlZCBiYXIgY2hhcnQKcDIgPC0gcDIgKyBnZ3RpdGxlKCJNYXJpdGFsIFN0YXR1cyBieSBOZXh0IE1vbnRoIERlZmF1bHQiKSArIHhsYWIoIiIpICsgeWxhYigiTkVYVF9NT05USF9ERUZBVUxUIikgIyBBZGRzIHRpdGxlcwpwMiA8LSBwMiArIGZhY2V0X2dyaWQoZmFjZXRzPS4gfiBORVhUX01PTlRIX0RFRkFVTFQpICMgU2lkZSBieSBzaWRlIGJhciBjaGFydApwMiA8LSBwMiArIGNvb3JkX3BvbGFyKHRoZXRhPSJ5IikgIyBzaWRlIGJ5IHNpZGUgcGllIGNoYXJ0CnAyCmBgYApfKkFzIGl0IGNhbiBiZSBjbGVhcmx5IHNlZW4gaGVyZSwgbWFyaXRhbCBzdGF0dXMgZG9lcyBub3QgaGF2ZSBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2Ugd2hlbiBjb21wYXJlZCB3aXRoIGRlZmF1bHRpbmcgY3JlZGl0IGNhcmRzIGFuZCBub3QgZGVmYXVsdGluZy4qXwotKkhlbmNlIHdlIGhhdmUgY2xlYXIgcmVhc29ucyBmb3IgcmVtb3ZpbmcgdGhlIHZhcmlhYmxlcyBnZW5kZXIgYW5kIG1hcml0YWwgc3RhdHVzKl8KCiMjIyBFZHVjYXRpb24gU3RhdHVzCmBgYHtyfQplZHVjYXRpb25fcmVzcG9uc2VfY3QgPC0gQ3Jvc3NUYWJsZSh0cmFpbl9kZiRFRFVDQVRJT05fU1RBVFVTLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5fZGYkTkVYVF9NT05USF9ERUZBVUxUICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3Aucj0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wLmM9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3AuY2hpc3E9RkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hpc3EgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcC50PVRSVUUpCmBgYApQbG90dGluZwpgYGB7cn0KZ2dwbG90KGRhdGE9YXMuZGF0YS5mcmFtZShlZHVjYXRpb25fcmVzcG9uc2VfY3QkcHJvcC50YmwpLAogICAgICAgYWVzKHg9eCx5PUZyZXEsZmlsbD15KSkrCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSAiZmlsbCIpKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpKwogIGNvb3JkX2ZsaXAoKSt0aGVtZV9taW5pbWFsKCkrCiAgeGxhYigiRWR1Y2F0aW9uIFN0YXR1cyIpK3lsYWIoIlBlcmNlbnRhZ2VzIikKYGBgCl8qRXZlbiB0aG91Z2ggaXQgaXMgbm90IGV2aWRlbnQgdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVkdWNhdGlvbiBzdGF0dXMgYW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSwgdGhlIGNoaSBzcXVhcmUgdGVzdCBjb25maXJtcyB0aGF0IHRoZXJlIGlzIGFuIGFzc29jaWF0aW9uIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMqXwojIyMgQmFsYW5jZSBMaW1pdApgYGB7cn0KYmx2IDwtIHRyYWluX2RmJT4lIAogIG11dGF0ZShCYWxhbmNlX0xpbWl0X1YxPXRyaW13cyhCYWxhbmNlX0xpbWl0X1YxKSkgJT4lIAogIHN1YnNldChzZWxlY3Q9QmFsYW5jZV9MaW1pdF9WMSkKdHJhaW5fZGYkQmFsYW5jZV9MaW1pdF9WMSA8LSBmYWN0b3IoYXMuZmFjdG9yKGJsdiRCYWxhbmNlX0xpbWl0X1YxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiMTAwSyIsIjIwMEsiLCIzMDBLIiwiNDAwSyIsIjUwMEsiLCIxTSIsIjEuNU0iLCIyLjVNIikpCmJhbGFuY2VfcmVzcG9uc2VfY3QgPC0gQ3Jvc3NUYWJsZSh0cmFpbl9kZiRCYWxhbmNlX0xpbWl0X1YxICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluX2RmJE5FWFRfTU9OVEhfREVGQVVMVCAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wLnI9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcC5jPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wLmNoaXNxPUZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoaXNxID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3AudD1UUlVFKQpgYGAKClBsb3R0aW5nCmBgYHtyfQpnZ3Bsb3QoZGF0YT1hcy5kYXRhLmZyYW1lKGJhbGFuY2VfcmVzcG9uc2VfY3QkcHJvcC50YmwpLAogICAgICAgYWVzKGZpbGw9eSx5PUZyZXEseD14KSkrCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSAiZmlsbCIpKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpKwogIGNvb3JkX2ZsaXAoKSt0aGVtZV9taW5pbWFsKCkrCiAgeGxhYigiQmFsYW5jZSBMaW1pdCIpK3lsYWIoIlBlcmNlbnRhZ2VzIikKYGBgCl8qSGVyZSBpdCBpcyBjbGVhciB0aGF0IGEgY2xpZW50IHdpdGggYSBsb3dlciBiYWxhbmNlIGxpbWl0IGhhcyBhIGhpZ2hlciBjaGFuY2Ugb2YgZ2V0dGluZyBhIGNyZWRpdCBjYXJkIGRlZmF1bHQuIEFsc28gZnJvbSB0aGUgY2hpIHNxdWFyZSB0ZXN0IGl0IGlzIGNsZWFyIHRoYXQgd2l0aCBhIHAtdmFsdWUgbGVzcyB0aGFuIGFscGhhPTAuMDUgd2UgY2FuIHJlamVjdCBIMCBhbmQgY29tZSB0byB0aGUgY29uY2x1c2lvbiB0aGF0IHRoZXJlIGlzIGJhbGFuY2UgbGltaXQgYW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSBhcmUgbm90IGluZGVwZW5kZW50Kl8KCiMjIyBQYXltZW50IGR1ZSB2YXJpYWJsZQoKYGBge3J9CmdncGxvdCh0cmFpbl9kZiAlPiUKICBnYXRoZXIoUEFZX0pVTFkgLCBQQVlfQVVHICwgUEFZX1NFUCAsIFBBWV9PQ1QgLCBQQVlfTk9WICwgUEFZX0RFQyAsIGtleSA9ICJQQVlfTU9OVEgiLHZhbHVlID0gIk5PX01PTlRIU19QQVkiKSwgYWVzKHggPSBOT19NT05USFNfUEFZLCB5ID0gUEFZX01PTlRILCBmaWxsID0gTkVYVF9NT05USF9ERUZBVUxUKSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoc2NhbGU9MC45LGFscGhhPTAuNykgKwogIHRoZW1lX3JpZGdlcygpKwogIGxhYnMoZmlsbD0nQ3JlZGl0IERlZmF1bHQnKSAKYGBgCgpgYGB7cn0KYGBgCgojIyBRdWFudGl0YXRpdmUgVmFyaWFibGVzCgojIyMgRHVlIGFtb3VudHMKYGBge3J9CmdncGxvdChkYXRhPWdhdGhlcih0cmFpbl9kZixrZXksdmFsdWUsYygiRFVFX0FNVF9KVUxZIiwiRFVFX0FNVF9BVUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRVRV9BTVRfU0VQIiwiRFVFX0FNVF9PQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRVRV9BTVRfTk9WIiwiRFVFX0FNVF9ERUMiKSkgJT4lIAogICAgICAgICBtdXRhdGUoa2V5ID0gZmFjdG9yKGtleSxsZXZlbHM9YygiRFVFX0FNVF9KVUxZIiwiRFVFX0FNVF9BVUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRVRV9BTVRfU0VQIiwiRFVFX0FNVF9PQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRVRV9BTVRfTk9WIiwiRFVFX0FNVF9ERUMiKSkpLAogICAgICAgYWVzKHggPSBrZXkseSA9IHZhbHVlLGZpbGw9TkVYVF9NT05USF9ERUZBVUxUKSkrCiAgZ2VvbV9ib3hwbG90KCkrCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiSlVMWSIsIkFVRyIsIlNFUCIsIk9DVCIsIk5PViIsIkRFQyIpKQpgYGAKCiMjIyBQYWlkIGFtb3VudHMKYGBge3J9CmdncGxvdChkYXRhPWdhdGhlcih0cmFpbl9kZixrZXksdmFsdWUsYygiUEFJRF9BTVRfSlVMWSIsIlBBSURfQU1UX0FVRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBBSURfQU1UX1NFUCIsIlBBSURfQU1UX09DVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBBSURfQU1UX05PViIsIlBBSURfQU1UX0RFQyIpKSAlPiUKICAgICAgICAgbXV0YXRlKGtleSA9IGZhY3RvcihrZXksbGV2ZWxzPWMoIlBBSURfQU1UX0pVTFkiLCJQQUlEX0FNVF9BVUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQQUlEX0FNVF9TRVAiLCJQQUlEX0FNVF9PQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQQUlEX0FNVF9OT1YiLCJQQUlEX0FNVF9ERUMiKSkpLAogICAgICAgYWVzKHggPSBrZXkseSA9IHZhbHVlLGZpbGw9TkVYVF9NT05USF9ERUZBVUxUKSkrCiAgZ2VvbV9ib3hwbG90KCkrCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiSlVMWSIsIkFVRyIsIlNFUCIsIk9DVCIsIk5PViIsIkRFQyIpKQpgYGAKCiMjIEZ1cnRoZXIgdmlzdWFsaXphdGlvbnMKCiMjIyBQYWlkIGFtb3VudCBhcyBhIHJhdGlvIG9mIGR1ZSBhbW91bnQKYGBge3J9CmVwczwtMC4xCnRyYWluX2RmJGR1ZXBhaWRfSlVMWSA8LSB0cmFpbl9kZiRQQUlEX0FNVF9KVUxZLyh0cmFpbl9kZiREVUVfQU1UX0pVTFkrZXBzKQp0cmFpbl9kZiRkdWVwYWlkX0FVRyA8LSAgdHJhaW5fZGYkUEFJRF9BTVRfQVVHLyh0cmFpbl9kZiREVUVfQU1UX0FVRytlcHMpCnRyYWluX2RmJGR1ZXBhaWRfU0VQIDwtICB0cmFpbl9kZiRQQUlEX0FNVF9TRVAvKHRyYWluX2RmJERVRV9BTVRfU0VQK2VwcykKdHJhaW5fZGYkZHVlcGFpZF9PQ1QgPC0gIHRyYWluX2RmJFBBSURfQU1UX09DVC8odHJhaW5fZGYkRFVFX0FNVF9PQ1QrZXBzKQp0cmFpbl9kZiRkdWVwYWlkX05PViA8LSAgdHJhaW5fZGYkUEFJRF9BTVRfTk9WLyh0cmFpbl9kZiREVUVfQU1UX05PVitlcHMpCnRyYWluX2RmJGR1ZXBhaWRfREVDIDwtICB0cmFpbl9kZiRQQUlEX0FNVF9ERUMvKHRyYWluX2RmJERVRV9BTVRfREVDK2VwcykKCnRyYWluX2RmJGR1ZXBhaWRfSlVMWVtpcy5uYW4odHJhaW5fZGYkZHVlcGFpZF9KVUxZKV0gPC0gMAp0cmFpbl9kZiRkdWVwYWlkX0FVR1tpcy5uYW4odHJhaW5fZGYkZHVlcGFpZF9BVUcpXSA8LSAwCnRyYWluX2RmJGR1ZXBhaWRfU0VQW2lzLm5hbih0cmFpbl9kZiRkdWVwYWlkX1NFUCldIDwtIDAKdHJhaW5fZGYkZHVlcGFpZF9PQ1RbaXMubmFuKHRyYWluX2RmJGR1ZXBhaWRfT0NUKV0gPC0gMAp0cmFpbl9kZiRkdWVwYWlkX05PVltpcy5uYW4odHJhaW5fZGYkZHVlcGFpZF9OT1YpXSA8LSAwCnRyYWluX2RmJGR1ZXBhaWRfREVDW2lzLm5hbih0cmFpbl9kZiRkdWVwYWlkX0RFQyldIDwtIDAKCm5ldzA0IDwtIGRhdGEuZnJhbWUodHJhaW5fZGYkZHVlcGFpZF9OT1YsdHJhaW5fZGYkZHVlcGFpZF9PQ1QsCiAgICAgICAgICAgICAgICAgICAgdHJhaW5fZGYkZHVlcGFpZF9TRVAsdHJhaW5fZGYkZHVlcGFpZF9BVUcsCiAgICAgICAgICAgICAgICAgICAgdHJhaW5fZGYkZHVlcGFpZF9KVUxZLHRyYWluX2RmJGR1ZXBhaWRfREVDLAogICAgICAgICAgICAgICAgICAgIGFzLmZhY3Rvcih0cmFpbl9kZiRORVhUX01PTlRIX0RFRkFVTFQpKQpuZXcwNSA8LSBuZXcwNCAlPiUKICBnYXRoZXIodHJhaW5fZGYuZHVlcGFpZF9ERUMsdHJhaW5fZGYuZHVlcGFpZF9OT1YsCiAgICAgICAgIHRyYWluX2RmLmR1ZXBhaWRfT0NULHRyYWluX2RmLmR1ZXBhaWRfU0VQLAogICAgICAgICB0cmFpbl9kZi5kdWVwYWlkX0FVRyx0cmFpbl9kZi5kdWVwYWlkX0pVTFksIGtleSA9ICJtb250aF9wYWlkX2R1ZSIsdmFsdWUgPSAicmF0aW8iKQoKbmV3MDYgPC0gbmEub21pdChuZXcwNSkKbmV3MDYkbW9udGhfcGFpZF9kdWUgPC0gZmFjdG9yKG5ldzA2JG1vbnRoX3BhaWRfZHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoInRyYWluX2RmLmR1ZXBhaWRfSlVMWSIsInRyYWluX2RmLmR1ZXBhaWRfQVVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cmFpbl9kZi5kdWVwYWlkX1NFUCIsInRyYWluX2RmLmR1ZXBhaWRfT0NUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cmFpbl9kZi5kdWVwYWlkX05PViIsInRyYWluX2RmLmR1ZXBhaWRfREVDIikpCmNvbG5hbWVzKG5ldzA2KVsxXSA8LSAicmVzcG9uc2UiCmdncGxvdChuZXcwNiwgYWVzKHggPSBtb250aF9wYWlkX2R1ZSAsIHkgPSByYXRpbykpICsgCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXJlc3BvbnNlKSkrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDEpKSsKICB0aGVtZV9taW5pbWFsKCkrCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiSlVMWSIsIkFVRyIsIlNFUCIsIk9DVCIsIk5PViIsIkRFQyIpKQpgYGAKIyMjIER1ZSBhbW91bnQgYXMgYSByYXRpbyBvZiBjcmVkaXQgbGltaXQKYGBge3J9CnRyYWluX2RmIDwtIHRyYWluX2RmICU+JSBtdXRhdGUoCiAgQmFsYW5jZV9DcmVkaXRfTGltaXRfTnVtZXJpYyA9IGNhc2Vfd2hlbigKICAgIEJhbGFuY2VfTGltaXRfVjEgPT0gIjEwMEsiIH4gMTAwMDAwLAogICAgQmFsYW5jZV9MaW1pdF9WMSA9PSAiMjAwSyIgfiAyMDAwMDAsCiAgICBCYWxhbmNlX0xpbWl0X1YxID09ICIzMDBLIiB+IDMwMDAwMCwKICAgIEJhbGFuY2VfTGltaXRfVjEgPT0gIjQwMEsiIH4gNDAwMDAwLAogICAgQmFsYW5jZV9MaW1pdF9WMSA9PSAiNTAwSyIgfiA1MDAwMDAsCiAgICBCYWxhbmNlX0xpbWl0X1YxID09ICIxTSIgfiAxMDAwMDAwLAogICAgQmFsYW5jZV9MaW1pdF9WMSA9PSAiMS41TSIgfiAxNTAwMDAwLAogICAgQmFsYW5jZV9MaW1pdF9WMSA9PSAiMk0iIH4gMjAwMDAwMCwKICAgIEJhbGFuY2VfTGltaXRfVjEgPT0gIjIuNU0iIH4gMjUwMDAwMAogICkKKSAlPiUKICBtdXRhdGUoCiAgICBEdWVfQ3JlZGl0X0xpbV9KVUxZPShEVUVfQU1UX0pVTFkvQmFsYW5jZV9DcmVkaXRfTGltaXRfTnVtZXJpYykgKiAxMDAsCiAgICBEdWVfQ3JlZGl0X0xpbV9BVUc9KERVRV9BTVRfQVVHL0JhbGFuY2VfQ3JlZGl0X0xpbWl0X051bWVyaWMpICogMTAwLAogICAgRHVlX0NyZWRpdF9MaW1fU0VQPShEVUVfQU1UX1NFUC9CYWxhbmNlX0NyZWRpdF9MaW1pdF9OdW1lcmljKSAqIDEwMCwKICAgIER1ZV9DcmVkaXRfTGltX09DVD0oRFVFX0FNVF9PQ1QvQmFsYW5jZV9DcmVkaXRfTGltaXRfTnVtZXJpYykgKiAxMDAsCiAgICBEdWVfQ3JlZGl0X0xpbV9OT1Y9KERVRV9BTVRfTk9WL0JhbGFuY2VfQ3JlZGl0X0xpbWl0X051bWVyaWMpICogMTAwLAogICAgRHVlX0NyZWRpdF9MaW1fREVDPShEVUVfQU1UX0RFQy9CYWxhbmNlX0NyZWRpdF9MaW1pdF9OdW1lcmljKSAqIDEwMAogICkgJT4lCiAgbmEub21pdCgpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGE9Z2F0aGVyKHRyYWluX2RmLGtleSx2YWx1ZSxjKCJEdWVfQ3JlZGl0X0xpbV9KVUxZIiwiRHVlX0NyZWRpdF9MaW1fQVVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEdWVfQ3JlZGl0X0xpbV9TRVAiLCJEdWVfQ3JlZGl0X0xpbV9PQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkR1ZV9DcmVkaXRfTGltX05PViIsIkR1ZV9DcmVkaXRfTGltX0RFQyIpKSAlPiUKICAgICAgICAgbXV0YXRlKGtleSA9IGZhY3RvcihrZXksbGV2ZWxzPWMoIkR1ZV9DcmVkaXRfTGltX0pVTFkiLCJEdWVfQ3JlZGl0X0xpbV9BVUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkR1ZV9DcmVkaXRfTGltX1NFUCIsIkR1ZV9DcmVkaXRfTGltX09DVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRHVlX0NyZWRpdF9MaW1fTk9WIiwiRHVlX0NyZWRpdF9MaW1fREVDIikpKSwKICAgICAgIGFlcyh4PWtleSx5PXZhbHVlKSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPU5FWFRfTU9OVEhfREVGQVVMVCkpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwyMDApKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCJKVUxZIiwiQVVHIiwiU0VQIiwiT0NUIiwiTk9WIiwiREVDIikpCmBgYAojIyMgUGFpZCBhbW91bnQgYXMgYSByYXRpbyBvZiBjcmVkaXQgbGltaXQKCiMjIyBCYWxhbmNlIGZlYXR1cmUKSGVyZSB3ZSBjb25zaWRlciB0aGUgYmFsYW5jZSBhcyB0aGUgYGBgRHVlIGFtb3VudCAtIFBhaWQgYW1vdW50YGBgCmBgYHtyfQp0cmFpbl9kZiRiYWxhbmNlX2p1bHkgPC0gdHJhaW5fZGYkRFVFX0FNVF9KVUxZIC0gdHJhaW5fZGYkUEFJRF9BTVRfSlVMWQp0cmFpbl9kZiRiYWxhbmNlX2F1ZyA8LSB0cmFpbl9kZiREVUVfQU1UX0FVRyAtIHRyYWluX2RmJFBBSURfQU1UX0FVRwp0cmFpbl9kZiRiYWxhbmNlX3NlcCA8LSB0cmFpbl9kZiREVUVfQU1UX1NFUCAtIHRyYWluX2RmJFBBSURfQU1UX1NFUAp0cmFpbl9kZiRiYWxhbmNlX29jdCA8LSB0cmFpbl9kZiREVUVfQU1UX09DVCAtIHRyYWluX2RmJFBBSURfQU1UX09DVAp0cmFpbl9kZiRiYWxhbmNlX25vdiA8LSB0cmFpbl9kZiREVUVfQU1UX05PViAtIHRyYWluX2RmJFBBSURfQU1UX05PVgp0cmFpbl9kZiRiYWxhbmNlX2RlYyA8LSB0cmFpbl9kZiREVUVfQU1UX0RFQyAtIHRyYWluX2RmJFBBSURfQU1UX0RFQwoKZ2dwbG90KGRhdGE9Z2F0aGVyKHRyYWluX2RmLGtleSx2YWx1ZSxjKCJiYWxhbmNlX2p1bHkiLCJiYWxhbmNlX2F1ZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmFsYW5jZV9zZXAiLCJiYWxhbmNlX29jdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmFsYW5jZV9ub3YiLCJiYWxhbmNlX2RlYyIpKSAlPiUKICAgICAgICAgbXV0YXRlKGtleSA9IGZhY3RvcihrZXksbGV2ZWxzID1jKCJiYWxhbmNlX2p1bHkiLCJiYWxhbmNlX2F1ZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmFsYW5jZV9zZXAiLCJiYWxhbmNlX29jdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmFsYW5jZV9ub3YiLCJiYWxhbmNlX2RlYyIpKSksCiAgICAgICBhZXMoeCA9IGtleSx5ID0gdmFsdWUpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9TkVYVF9NT05USF9ERUZBVUxUKSkrCiAgdGhlbWVfbWluaW1hbCgpKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIkpVTFkiLCJBVUciLCJTRVAiLCJPQ1QiLCJOT1YiLCJERUMiKSkKYGBgCiMjIyBCYWxhbmNlIGFzIGEgcmF0aW8gb2Ygb2YgY3JlZGl0IGxpbWl0CgpgYGB7cn0KdHJhaW5fZGYgPC0gdHJhaW5fZGYgJT4lIG11dGF0ZSgKICAgIGJhbGFuY2VfQ3JlZGl0X0xpbV9KVUxZPShiYWxhbmNlX2p1bHkvQmFsYW5jZV9DcmVkaXRfTGltaXRfTnVtZXJpYykgKiAxMDAsCiAgICBiYWxhbmNlX0NyZWRpdF9MaW1fQVVHPShiYWxhbmNlX2F1Zy9CYWxhbmNlX0NyZWRpdF9MaW1pdF9OdW1lcmljKSAqIDEwMCwKICAgIGJhbGFuY2VfQ3JlZGl0X0xpbV9TRVA9KGJhbGFuY2Vfc2VwL0JhbGFuY2VfQ3JlZGl0X0xpbWl0X051bWVyaWMpICogMTAwLAogICAgYmFsYW5jZV9DcmVkaXRfTGltX09DVD0oYmFsYW5jZV9vY3QvQmFsYW5jZV9DcmVkaXRfTGltaXRfTnVtZXJpYykgKiAxMDAsCiAgICBiYWxhbmNlX0NyZWRpdF9MaW1fTk9WPShiYWxhbmNlX25vdi9CYWxhbmNlX0NyZWRpdF9MaW1pdF9OdW1lcmljKSAqIDEwMCwKICAgIGJhbGFuY2VfQ3JlZGl0X0xpbV9ERUM9KGJhbGFuY2VfZGVjL0JhbGFuY2VfQ3JlZGl0X0xpbWl0X051bWVyaWMpICogMTAwCiAgKSAlPiUKICBuYS5vbWl0KCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YT1nYXRoZXIodHJhaW5fZGYsa2V5LHZhbHVlLGMoImJhbGFuY2VfQ3JlZGl0X0xpbV9KVUxZIiwiYmFsYW5jZV9DcmVkaXRfTGltX0FVRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmFsYW5jZV9DcmVkaXRfTGltX1NFUCIsImJhbGFuY2VfQ3JlZGl0X0xpbV9PQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImJhbGFuY2VfQ3JlZGl0X0xpbV9OT1YiLCJiYWxhbmNlX0NyZWRpdF9MaW1fREVDIikpICU+JQogICAgICAgICBtdXRhdGUoa2V5ID0gZmFjdG9yKGtleSxsZXZlbHM9YygiYmFsYW5jZV9DcmVkaXRfTGltX0pVTFkiLCJiYWxhbmNlX0NyZWRpdF9MaW1fQVVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiYWxhbmNlX0NyZWRpdF9MaW1fU0VQIiwiYmFsYW5jZV9DcmVkaXRfTGltX09DVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYmFsYW5jZV9DcmVkaXRfTGltX05PViIsImJhbGFuY2VfQ3JlZGl0X0xpbV9ERUMiKSkpLAogICAgICAgYWVzKHg9a2V5LHk9dmFsdWUpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9TkVYVF9NT05USF9ERUZBVUxUKSkrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDIwMCkpKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIkpVTFkiLCJBVUciLCJTRVAiLCJPQ1QiLCJOT1YiLCJERUMiKSkKYGBgCiMjIyBQYXkgZGVsYXkgd2l0aCB0aGUgUGFpZCB2YWx1ZQpgYGB7cn0KZ2dwbG90KGRhdGE9dHJhaW5fZGYlPiUKICAgICAgICAgZ2F0aGVyKHBheV9rZXkscGF5X3ZhbHVlLGMoIlBBWV9KVUxZIiwiUEFZX0FVRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEFZX1NFUCIsIlBBWV9PQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBBWV9OT1YiLCJQQVlfREVDIikpICU+JQogICAgICAgICBnYXRoZXIocGFpZF9rZXkscGFpZF92YWx1ZSxjKCJQQUlEX0FNVF9KVUxZIiwiUEFJRF9BTVRfQVVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEFJRF9BTVRfU0VQIiwiUEFJRF9BTVRfT0NUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEFJRF9BTVRfTk9WIiwiUEFJRF9BTVRfREVDIikpICU+JQogICAgICAgICBtdXRhdGUocGF5X2tleSA9IGZhY3RvcihwYXlfa2V5LGxldmVscz1jKCJQQVlfSlVMWSIsIlBBWV9BVUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBBWV9TRVAiLCJQQVlfT0NUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQQVlfTk9WIiwiUEFZX0RFQyIpKSwKICAgICAgICAgICAgICAgIHBheV92YWx1ZT1mYWN0b3IocGF5X3ZhbHVlLGxldmVscz1zZXEoLTIsOSkpKSwKICAgICAgIGFlcyh5PXBhaWRfdmFsdWUseD1wYXlfdmFsdWUsZmlsbD1ORVhUX01PTlRIX0RFRkFVTFQpKSsKICAjIGdlb21fbGluZShhZXMoY29sb3I9TkVYVF9NT05USF9ERUZBVUxUKSkrCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSsKICBmYWNldF9ncmlkKHBheV9rZXkgfiAuKSArIHRoZW1lX21pbmltYWwoKQpgYGAKIyMjIENvcnJlbGF0aW9uIG1hdHJpeCBwbG90CgpEaXNyZWdhcmQgdGhpcyBhcyBpdCBpcyB0b28gY2x1dHRlcmVkCmBgYHtyfQp0cmFpbl9kZl90b19jb3IgPC0gdHJhaW5fZGYgJT4lIG11dGF0ZV9pZihpcy5mYWN0b3IsYXMubnVtZXJpYykKY29ycm1hdCA8LSBjb3IodHJhaW5fZGZfdG9fY29yLG1ldGhvZD0ic3BlYXJtYW4iKQptZWx0ZWQgPC0gbWVsdChjb3JybWF0KSAlPiUKICBtdXRhdGUodGV4dCA9IHBhc3RlMCgieDogIiwgVmFyMSwgIlxuIiwgInk6ICIsIFZhcjIsICJcbiIsICJWYWx1ZTogIixyb3VuZCh2YWx1ZSwyKSwgIlxuIikpCnAgPC0gZ2dwbG90KG1lbHRlZCwgYWVzKFZhcjEsIFZhcjIsIGZpbGw9IHZhbHVlLCB0ZXh0PXRleHQpKSArIAogIGdlb21fdGlsZSgpCnAKZ2dwbG90bHkocCwgdG9vbHRpcD0idGV4dCIpCmBgYAo=